package com.power.leopard.remote;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import com.power.leopard.util.InvocationUtils;

public class ServiceMethod {

	private Object service;
	private Method method;
	private Class<?>[] originParameterTypes;
	private Class<?>[] regulateParameterTypes;
	private int parameterTypeLength = 0;
	private boolean needCastParameterClasses = false;
	private Class<?>[] parameterClasses;

	public Object getService() {
		return service;
	}

	public void setService(Object service) {
		this.service = service;
	}

	public ServiceMethod(Method method, Object service) {
		super();
		this.method = method;
		this.service = service;

		originParameterTypes = method.getParameterTypes();
		regulateParameterTypes = regulateParameterTypes(method);
		parameterTypeLength = originParameterTypes.length;
		this.parameterClasses = regulateTypes(this.method.getParameterTypes());
	}

	private Class<?>[] regulateTypes(Class<?>[] types) {
		for (int i = 0; i < types.length; i++) {
			if (types[i] == byte.class) {
				types[i] = Byte.class;
			} else if (types[i] == short.class) {
				types[i] = Short.class;
			} else if (types[i] == int.class) {
				types[i] = Integer.class;
			} else if (types[i] == boolean.class) {
				types[i] = Boolean.class;
			} else if (types[i] == long.class) {
				types[i] = Long.class;
			} else if (types[i] == float.class) {
				types[i] = Float.class;
			} else if (types[i] == double.class) {
				types[i] = Double.class;
			}
		}
		return types;
	}

	private Class<?>[] regulateParameterTypes(Method method) {
		Class<?>[] parameterTypes = method.getParameterTypes();

		for (int i = 0; i < parameterTypes.length; i++) {
			if (parameterTypes[i] == boolean.class) {
				parameterTypes[i] = Boolean.class;
			} else if (parameterTypes[i] == byte.class) {
				parameterTypes[i] = Byte.class;
			} else if (parameterTypes[i] == short.class) {
				parameterTypes[i] = Short.class;
			} else if (parameterTypes[i] == float.class) {
				parameterTypes[i] = Float.class;
			} else if (parameterTypes[i] == double.class) {
				parameterTypes[i] = Double.class;
			} else if (parameterTypes[i] == int.class) {
				parameterTypes[i] = Integer.class;
			} else if (parameterTypes[i] == long.class) {
				parameterTypes[i] = Long.class;
			}
		}
		return parameterTypes;
	}

	public Object invoke(Object[] arguments) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		if (needCastParameterClasses && arguments != null && parameterClasses != null) {
			if (parameterClasses.length == arguments.length) {
				Object[] argumentsCast = new Object[arguments.length];
				for (int i = 0; i < arguments.length; i++) {
					Object arg = arguments[i];
					if (arg != null) {
						Class<?> argClass = arg.getClass();
						if (argClass != parameterClasses[i]) {
							if (argClass.equals(Double.class) && parameterClasses[i].equals(Float.class)) {
								arg = ((Double) arg).floatValue();
							} else if (argClass.equals(Integer.class) && parameterClasses[i].equals(Short.class)) {
								arg = ((Integer) arg).shortValue();
							}
						}
					}
					argumentsCast[i] = arg;
				}
				try {
					return this.getMethod().invoke(this.getService(), argumentsCast);
				} catch (IllegalArgumentException e) {
					throw new IllegalArgumentException("invalid parameter types:"
							+ InvocationUtils.getRemoteCallFullName(this.getMethod().getName(), argumentsCast), e.getCause());
				}
			}
		}
		try {
			return this.getMethod().invoke(this.getService(), arguments);
		} catch (IllegalArgumentException e) {
			throw new IllegalArgumentException("invalid parameter types:" + InvocationUtils.getRemoteCallFullName(this.getMethod().getName(), arguments),
					e.getCause());
		}

	}

	public Method getMethod() {
		return method;
	}

	public void setMethod(Method method) {
		this.method = method;
	}

	public Class<?>[] getOriginParameterTypes() {
		return originParameterTypes;
	}

	public void setOriginParameterTypes(Class<?>[] originParameterTypes) {
		this.originParameterTypes = originParameterTypes;
	}

	public Class<?>[] getRegulateParameterTypes() {
		return regulateParameterTypes;
	}

	public void setRegulateParameterTypes(Class<?>[] regulateParameterTypes) {
		this.regulateParameterTypes = regulateParameterTypes;
	}

	public int getParameterTypeLength() {
		return parameterTypeLength;
	}

	public void setParameterTypeLength(int parameterTypeLength) {
		this.parameterTypeLength = parameterTypeLength;
	}

	public void setNeedCastParameterClasses(boolean needCastParameterClasses) {
		this.needCastParameterClasses = needCastParameterClasses;
	}

}
